library(rental)
library(cancensus)
library(tidyverse)
library(sf)
Listings Overview
As an example we read the August data for unfurnished listings for Vancouver, Calgary and Toronto.
Reading regions list from local cache.
Cached regions list may be out of date. Set `use_cache = FALSE` to update it.Reading geo data from local cache.
[1] "Toronto (C)"
[1] "Calgary (CY)"
[1] "Vancouver (CY)"
Vancouver over time
regions=list_census_regions('CA16', use_cache = TRUE) %>% filter(level=="CSD",name == "Vancouver")
Reading regions list from local cache.
geo=get_census(dataset = 'CA16',regions=as_census_region_list(regions),geo_format='sf',level="Regions")
Reading geo data from local cache.
start_time="2017-05-01"
end_time="2018-03-04"
ls <- get_listings(start_time,end_time,geo$geometry,filter = 'unfurnished')
summary=ls %>% as.data.frame %>% select("price","beds") %>% group_by(beds) %>% summarize(median=paste0("$",format(median(price),big.mark=",")), count=n()) %>% mutate(name="Vancouver")
Fold in nbhd data
ls<-st_as_sf(ls) %>%
st_join(
st_read(file.path(getOption("custom_data_path"),"local_area_boundary_shp/local_area_boundary.shp")) %>%
st_transform(4326))
Reading layer `local_area_boundary' from data source `/Users/jens/.custom_data/local_area_boundary_shp/local_area_boundary.shp' using driver `ESRI Shapefile'
Simple feature collection with 22 features and 2 fields
geometry type: POLYGON
dimension: XY
bbox: xmin: 483644.8 ymin: 5449580 xmax: 498313 ymax: 5460349
epsg (SRID): 26910
proj4string: +proj=utm +zone=10 +datum=NAD83 +units=m +no_defs
although coordinates are longitude/latitude, st_intersects assumes that they are planar
ggplot(ls %>%
filter(as.integer(beds)<3, as.integer(beds)>0, price<6000, price>500),
aes(x=post_date, y=price, color=beds,group=beds)) +
#geom_point(size=0.01,alpha=0.2) +
geom_smooth(method = "loess", se = FALSE) +
scale_color_discrete(name="Bedrooms") +
theme_bw() +
facet_wrap("NAME", scales = "free_y") +
labs(title=paste0("Listings (unfurnished) ",format(nrow(ls))),x="Date",y="Asking Rent")

#ggplot(nbhds, aes(fill=NAME)) +geom_sf()
area="Renfrew-Collingwood"
plot_data <- ls %>% filter(NAME==area,as.integer(beds)<3, price<5000) %>% mutate(beds=ifelse(as.integer(beds)>=5,"5+",beds))
ggplot(plot_data, aes(x=post_date, y=price, color=beds,group=beds)) +
geom_point(size=0.01,alpha=0.2) +
geom_smooth(method = "loess", se = FALSE) +
scale_color_discrete(name="Bedrooms") +
theme_bw() +
labs(title=paste0(area," Listings (unfurnished) ",format(nrow(plot_data))),x="Date",y="Asking Rent")

NA
plot_data <- ls %>% filter(as.integer(beds)<3, price<6000, price>500) %>% mutate(ppsf=price/size) %>% filter(!is.na(ppsf) & ppsf<7.5)
ggplot(plot_data, aes(x=post_date, y=ppsf, color=beds,group=beds)) +
geom_point(size=0.01,alpha=0.2) +
geom_smooth(method = "loess", se = FALSE) +
theme_bw()

plot_data <- ls %>% filter(as.integer(beds)<=2) %>% mutate(beds=ifelse(as.integer(beds)>=5,"5+",beds),
type=paste0(beds, ifelse(downtown," downtown","")))
# totals <- plot_data %>%
# group_by(downtown,beds) %>% summarize(price,sub)
ggplot(plot_data, aes(x=post_date, y=price, color=type,group=type)) +
geom_point(size=0.01,alpha=0.2) +
scale_color_brewer(palette = "Paired") +
geom_smooth(method = "loess", se = FALSE) +
theme_bw()

Map
library(sf)
library(ggplot2)
geo=get_census(dataset = 'CA16',regions=list(CMA="59933"),geo_format='sf',level="Regions")
ls <- get_listings(start_time,end_time,geo$geometry,beds=c(1),filter = 'unfurnished')
summary=ls %>% as.data.frame %>% select("price","beds") %>% group_by(beds) %>% summarize(median=paste0("$",format(median(price),big.mark=","))) %>% mutate(name=row$name)
cts=get_census(dataset = 'CA16',regions=list(CMA="59933"),geo_format='sf',level="CSD")
min_listings=10
median_rent <- function(v){
result <- ifelse(length(v)>min_listings, median(v),NA)
return(result)
}
aggregate_listings <- aggregate(cts %>% select("GeoUID"),ls,function(x){x})
data <- aggregate(ls %>% select("price"),cts,median_rent)
cutoffs=as.integer(quantile(data$price, probs=seq(0,1,0.1), na.rm=TRUE))
labels=factor(as.character(seq(1,length(cutoffs)-1) %>% lapply(function(i){return(paste0(cutoffs[i]," - ",cutoffs[i+1]))})),order=TRUE)
colors=setNames(RColorBrewer::brewer.pal(length(labels),"RdYlBu"),labels)
data$discrete_price= data$price %>% cut(breaks=cutoffs, labels=labels)
ggplot() +
geom_sf(data=cts, fill="#808080", size=0.1) +
geom_sf(data=data, aes(fill = discrete_price), size=0.1) +
scale_fill_brewer(palette='RdYlBu', direction=-1, na.value="#808080",name="Median Price") +
labs(title="August Studio and 1 Bedroom Unfurnished Median Ask") +
theme_opts

Rent distributions by municipality

Looking into Coquitlam
region_name="Coquitlam" #"Richmond"
regions=as_census_region_list(search_census_regions(region_name,'CA16','CSD') %>% filter(name==region_name))
geo=get_census(dataset = 'CA16',regions=regions,geo_format='sf',level="Regions")
ls <- get_listings(start_time,end_time,geo$geometry,beds=c(1),filter = 'unfurnished',sanity=c(400,4000))
summary=ls %>% as.data.frame %>%
select("price","beds") %>%
group_by(beds) %>%
summarize(median=paste0("$",format(median(price),big.mark=","))) %>%
mutate(name=row$name)
cutoffs=c(400,1350,4000)
labels=factor(as.character(seq(1,length(cutoffs)-1) %>% lapply(function(i){return(paste0(cutoffs[i]," - ",cutoffs[i+1]))})),order=TRUE)
colors=setNames(c("turquoise","purple"),labels)
ls$discrete_price= ls$price %>% cut(breaks=cutoffs, labels=labels)
#ls <- cbind(ls,st_coordinates(st_transform(ls,102002)$location))
ls <- cbind(ls,st_coordinates(ls$location))
library(ggmap)
base <- get_map(paste0(region_name,", Canada"), zoom=12, source = "stamen", maptype = "toner",
crop = T)
#ggplot() +
ggmap(base) +
#geom_sf(data=geo, fill="#808080", size=0.1) +
#coord_sf(crs=st_crs(102002)) +
geom_point(data=ls , aes(color = discrete_price, x=X, y=Y), shape=21, size=2) +
scale_fill_manual(palette=colors) +
labs(title="August Studio and 1 Bedroom Unfurnished Median Ask",color="Price") +
theme_opts

Rent distributions over time
region_name="Vancouver"
regions=as_census_region_list(search_census_regions(region_name,'CA16','CSD') %>% filter(name==region_name))
geo=get_census(dataset = 'CA16',regions=regions,geo_format='sf',level="Regions")
ls <- get_listings(start_time,end_time,geo$geometry,beds=c(1),filter = 'unfurnished',sanity=c(400,4000))
ls$year_month <- factor(substr(ls$post_date,0,7),ordered = TRUE)
#ls$year_month_day <- factor(substr(ls$post_date,0,10),ordered = TRUE)
#ls %>% group_by(year_month) %>% summarize(count=length(year_month)) %>% as.data.frame %>% select("year_month","count")
#ls %>% group_by(year_month_day) %>% summarize(count=length(year_month_day)) %>% as.data.frame %>% select("year_month_day","count")
plot_data <- ls %>% as.data.frame %>% select("price","year_month")
title="Distribution of Unfurnished 1br Rents, City of Vancouver"
p1 <- ggplot(plot_data) +
geom_density(aes(x=price, color=year_month)) +
labs(title=title, color="Year-Month")
p2 <- ggplot(plot_data, aes(year_month, price))+
geom_violin(aes(fill=year_month )) +
#geom_beeswarm(pch = 1, col='white', cex=0.8, alpha=0.6) +
labs(title=title, fill="Year-Month", x="Year-Month")
grid.arrange(p1, p2, ncol=1)

Rent distributions by municipality
library(ggbeeswarm)
library(gridExtra)
region_names=c("Vancouver","Toronto","Victoria","Calgary")
regions= as_census_region_list(do.call(rbind,lapply(region_names,function(region_name){return((search_census_regions(region_name,'CA16','CSD') %>% filter(name==region_name)))})))
geo=get_census(dataset = 'CA16',regions=regions,geo_format='sf',level="Regions")
geometry=st_union(geo$geometry)
beds=2
ls <- get_listings(start_time,end_time,geometry,beds=c(beds),filter = 'unfurnished',sanity=c(400,5000))
summary=ls %>% as.data.frame %>% select("price","beds") %>% group_by(beds) %>% summarize(median=paste0("$",format(median(price),big.mark=","))) %>% mutate(name=row$name)
geos=get_census(dataset = 'CA16',regions=regions,geo_format='sf',level="CSD") %>%
st_join(ls)
plot_data <- geos %>% as.data.frame %>% select("name","price") %>%
rename(Municipality=name)
title=paste0("Distribution of Unfurnished ",beds,"br Rents, August 2017")
p1 <- ggplot(plot_data) +
geom_density(aes(x=price, color=Municipality)) +
labs(title=title)
p2 <- ggplot(plot_data, aes(Municipality, price))+
geom_violin(aes(fill=Municipality )) +
#geom_beeswarm(pch = 1, col='white', cex=0.8, alpha=0.6) +
labs(title=title)
grid.arrange(p1, p2, ncol=1)

Checking Specific area
#geo=sf::read_sf("../data/custom_region.geojson")
geo=sf::read_sf("../data/victoria_stainsbury.geojson")
ls <- get_listings("2017-06-01","2017-09-01",geo$geometry,filter = 'unfurnished')
summary=ls %>% as.data.frame %>% select("price","beds") %>% group_by(beds) %>% summarize(median=paste0("$",format(median(price),big.mark=","))) %>% mutate(name=row$name)
ggplot(ls, aes(beds, price))+
geom_violin(aes(fill=beds )) +
#geom_beeswarm(pch = 1, col='white', cex=0.8, alpha=0.6) +
labs(title="June-August 1br unfurnished Custom Region")

NA
cutoffs=c(400,1050,4000)
labels=factor(as.character(seq(1,length(cutoffs)-1) %>% lapply(function(i){return(paste0(cutoffs[i]," - ",cutoffs[i+1]))})),order=TRUE)
colors=setNames(c("turquoise","purple"),labels)
ls$discrete_price= ls$price %>% cut(breaks=cutoffs, labels=labels)
ls <- cbind(ls,st_coordinates(ls$location))
base <- get_map(location=c(-122.84594535827637, 49.18422801616818), zoom=15, source = "stamen", maptype = "toner",
crop = T)
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=49.184228,-122.845945&zoom=15&size=640x640&scale=2&maptype=terrain&sensor=false
Map from URL : http://tile.stamen.com/toner/15/5201/11226.png
Map from URL : http://tile.stamen.com/toner/15/5202/11226.png
Map from URL : http://tile.stamen.com/toner/15/5203/11226.png
Map from URL : http://tile.stamen.com/toner/15/5201/11227.png
Map from URL : http://tile.stamen.com/toner/15/5202/11227.png
Map from URL : http://tile.stamen.com/toner/15/5203/11227.png
Map from URL : http://tile.stamen.com/toner/15/5201/11228.png
Map from URL : http://tile.stamen.com/toner/15/5202/11228.png
Map from URL : http://tile.stamen.com/toner/15/5203/11228.png
#ggplot() +
ggmap(base) +
#geom_sf(data=geo, fill="#808080", size=0.1) +
#coord_sf(crs=st_crs(102002)) +
geom_point(data=ls , aes(color = discrete_price, x=X, y=Y), shape=21, size=4) +
scale_fill_manual(palette=colors) +
geom_polygon(data= fortify(as(geo,"Spatial")), aes(x=long, y=lat), fill=NA, size=0.5,color='blue') +
labs(title="August 1 Bedroom Unfurnished Median Ask",color="Price") +
theme_opts
Regions defined for each Polygons

my_theme <- list(
theme_minimal(),
theme(axis.text = element_blank(),
axis.title = element_blank(),
axis.ticks = element_blank(),
panel.grid.major = element_line(colour = "white"),
panel.grid.minor = element_line(colour = "white"),
panel.background = element_blank(),
axis.line = element_blank())
)
Vancouver rent map
Reading regions list from local cache.
Reading geo data from local cache.
the condition has length > 1 and only the first element will be usedalthough coordinates are longitude/latitude, st_intersects assumes that they are planar

LS0tCnRpdGxlOiAiUmVudGFsIExpc3RpbmdzIERlbW8iCmF1dGhvcjogIkplbnMgdm9uIEJlcmdtYW5uIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAp2aWduZXR0ZTogPgogICVcVmlnbmV0dGVJbmRleEVudHJ5e1JlbnRhbCBMaXN0aW5ncyBEZW1vfQogICVcVmlnbmV0dGVFbmdpbmV7a25pdHI6OnJtYXJrZG93bn0KICAlXFZpZ25ldHRlRW5jb2Rpbmd7VVRGLTh9Ci0tLQoKYGBge3Igc2V0dXAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkocmVudGFsKQpsaWJyYXJ5KGNhbmNlbnN1cykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoc2YpCmBgYAoKCiMjIExpc3RpbmdzIE92ZXJ2aWV3CkFzIGFuIGV4YW1wbGUgd2UgcmVhZCB0aGUgQXVndXN0IGRhdGEgZm9yIHVuZnVybmlzaGVkIGxpc3RpbmdzIGZvciBWYW5jb3V2ZXIsIENhbGdhcnkgYW5kIFRvcm9udG8uCmBgYHtyLCBlY2hvPUZBTFNFfQoKcmVnaW9uX25hbWVzPWMoIlZhbmNvdXZlciIsIkNhbGdhcnkiLCJUb3JvbnRvIikKcmVnaW9ucz1saXN0X2NlbnN1c19yZWdpb25zKCdDQTE2JywgdXNlX2NhY2hlID0gVFJVRSkgJT4lIGZpbHRlcihsZXZlbD09IkNTRCIsbmFtZSAlaW4lIHJlZ2lvbl9uYW1lcykKZ2VvPWdldF9jZW5zdXMoZGF0YXNldCA9ICdDQTE2JyxyZWdpb25zPWFzX2NlbnN1c19yZWdpb25fbGlzdChyZWdpb25zKSxnZW9fZm9ybWF0PSdzZicsbGV2ZWw9IlJlZ2lvbnMiKQoKc3RhcnRfdGltZT0iMjAxOC0wMi0wMSIKZW5kX3RpbWU9IjIwMTgtMDMtMzEiCgpmb3IgKGkgaW4gMTpucm93KGdlbykpIHsKICByb3c9Z2VvW2ksXQogIGxzIDwtIGdldF9saXN0aW5ncyhzdGFydF90aW1lLGVuZF90aW1lLHJvdyRnZW9tZXRyeSkKICBzdW1tYXJ5PWxzICU+JSBhcy5kYXRhLmZyYW1lICU+JSBzZWxlY3QoInByaWNlIiwiYmVkcyIsInNpemUiLCJmdXJuaXNoZWQiKSAlPiUgZ3JvdXBfYnkoYmVkcyxmdXJuaXNoZWQpICU+JSBzdW1tYXJpemUobWVkaWFuPXBhc3RlMCgiJCIsZm9ybWF0KG1lZGlhbihwcmljZSksYmlnLm1hcms9IiwiKSksbWVkaWFuX3Bwc2Y9cGFzdGUwKCIkIixyb3VuZChtZWRpYW4ocHJpY2Uvc2l6ZSwgbmEucm09VFJVRSksMiksIi9zZiIpLCBjb3VudD1uKCkpICU+JSBtdXRhdGUobmFtZT1yb3ckbmFtZSkKICBwcmludChyb3ckbmFtZSkKICBwcmludChzdW1tYXJ5KQogICNwcmludChwYXN0ZTAocm93JG5hbWUsIiBtZWRpYW4gcHJpY2U6ICQiLGZvcm1hdChtZWRpYW4obHMkcHJpY2UpLGJpZy5tYXJrPSIsIikpKQp9CgojbHMgJT4lIGZpbHRlcihiZWRzPT0iMSIpCgpgYGAKCgojIyBWYW5jb3V2ZXIgb3ZlciB0aW1lCmBgYHtyfQpyZWdpb25zPWxpc3RfY2Vuc3VzX3JlZ2lvbnMoJ0NBMTYnLCB1c2VfY2FjaGUgPSBUUlVFKSAlPiUgZmlsdGVyKGxldmVsPT0iQ1NEIixuYW1lID09ICJWYW5jb3V2ZXIiKQpnZW89Z2V0X2NlbnN1cyhkYXRhc2V0ID0gJ0NBMTYnLHJlZ2lvbnM9YXNfY2Vuc3VzX3JlZ2lvbl9saXN0KHJlZ2lvbnMpLGdlb19mb3JtYXQ9J3NmJyxsZXZlbD0iUmVnaW9ucyIpCgpzdGFydF90aW1lPSIyMDE3LTA1LTAxIgplbmRfdGltZT0iMjAxOC0wMy0wNCIKCgpscyA8LSBnZXRfbGlzdGluZ3Moc3RhcnRfdGltZSxlbmRfdGltZSxnZW8kZ2VvbWV0cnksZmlsdGVyID0gJ3VuZnVybmlzaGVkJykKc3VtbWFyeT1scyAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgc2VsZWN0KCJwcmljZSIsImJlZHMiKSAlPiUgZ3JvdXBfYnkoYmVkcykgJT4lIHN1bW1hcml6ZShtZWRpYW49cGFzdGUwKCIkIixmb3JtYXQobWVkaWFuKHByaWNlKSxiaWcubWFyaz0iLCIpKSwgY291bnQ9bigpKSAlPiUgbXV0YXRlKG5hbWU9IlZhbmNvdXZlciIpCiAgCmBgYAogRm9sZCBpbiBuYmhkIGRhdGEKYGBge3J9CmxzPC1zdF9hc19zZihscykgJT4lCiAgc3Rfam9pbigKICAgIHN0X3JlYWQoZmlsZS5wYXRoKGdldE9wdGlvbigiY3VzdG9tX2RhdGFfcGF0aCIpLCJsb2NhbF9hcmVhX2JvdW5kYXJ5X3NocC9sb2NhbF9hcmVhX2JvdW5kYXJ5LnNocCIpKSAlPiUgCiAgICAgIHN0X3RyYW5zZm9ybSg0MzI2KSkKYGBgCiAKCgpgYGB7cn0KCgpnZ3Bsb3QobHMgJT4lIAogICAgICAgICBmaWx0ZXIoYXMuaW50ZWdlcihiZWRzKTwzLCBhcy5pbnRlZ2VyKGJlZHMpPjAsIHByaWNlPDYwMDAsIHByaWNlPjUwMCksCiAgICAgICBhZXMoeD1wb3N0X2RhdGUsIHk9cHJpY2UsIGNvbG9yPWJlZHMsZ3JvdXA9YmVkcykpICsKICAjZ2VvbV9wb2ludChzaXplPTAuMDEsYWxwaGE9MC4yKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgc2UgPSBGQUxTRSkgKwogICAgc2NhbGVfY29sb3JfZGlzY3JldGUobmFtZT0iQmVkcm9vbXMiKSArCiAgICB0aGVtZV9idygpICsKICBmYWNldF93cmFwKCJOQU1FIiwgc2NhbGVzID0gImZyZWVfeSIpICsKICAgIGxhYnModGl0bGU9cGFzdGUwKCJMaXN0aW5ncyAodW5mdXJuaXNoZWQpICIsZm9ybWF0KG5yb3cobHMpKSkseD0iRGF0ZSIseT0iQXNraW5nIFJlbnQiKQoKI2dncGxvdChuYmhkcywgYWVzKGZpbGw9TkFNRSkpICtnZW9tX3NmKCkKCgpgYGAKCmBgYHtyfQphcmVhPSJSZW5mcmV3LUNvbGxpbmd3b29kIgpwbG90X2RhdGEgPC0gbHMgJT4lIGZpbHRlcihOQU1FPT1hcmVhLGFzLmludGVnZXIoYmVkcyk8MywgcHJpY2U8NTAwMCkgJT4lIG11dGF0ZShiZWRzPWlmZWxzZShhcy5pbnRlZ2VyKGJlZHMpPj01LCI1KyIsYmVkcykpCiAgZ2dwbG90KHBsb3RfZGF0YSwgYWVzKHg9cG9zdF9kYXRlLCB5PXByaWNlLCBjb2xvcj1iZWRzLGdyb3VwPWJlZHMpKSArIAogIGdlb21fcG9pbnQoc2l6ZT0wLjAxLGFscGhhPTAuMikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIHNlID0gRkFMU0UpICsKICAgIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWU9IkJlZHJvb21zIikgKwogICAgdGhlbWVfYncoKSArCiAgICBsYWJzKHRpdGxlPXBhc3RlMChhcmVhLCIgTGlzdGluZ3MgKHVuZnVybmlzaGVkKSAiLGZvcm1hdChucm93KHBsb3RfZGF0YSkpKSx4PSJEYXRlIix5PSJBc2tpbmcgUmVudCIpCiAgCmBgYAoKCgpgYGB7cn0KcGxvdF9kYXRhIDwtIGxzICU+JSBmaWx0ZXIoYXMuaW50ZWdlcihiZWRzKTwzLCAgcHJpY2U8NjAwMCwgcHJpY2U+NTAwKSAlPiUgbXV0YXRlKHBwc2Y9cHJpY2Uvc2l6ZSkgJT4lIGZpbHRlcighaXMubmEocHBzZikgJiBwcHNmPDcuNSkKICBnZ3Bsb3QocGxvdF9kYXRhLCBhZXMoeD1wb3N0X2RhdGUsIHk9cHBzZiwgY29sb3I9YmVkcyxncm91cD1iZWRzKSkgKyAKICBnZW9tX3BvaW50KHNpemU9MC4wMSxhbHBoYT0wLjIpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzZSA9IEZBTFNFKSArCiAgICB0aGVtZV9idygpCgpgYGAKCgpgYGB7cn0KcGxvdF9kYXRhIDwtIGxzICU+JSBmaWx0ZXIoYXMuaW50ZWdlcihiZWRzKTw9MikgJT4lIG11dGF0ZShiZWRzPWlmZWxzZShhcy5pbnRlZ2VyKGJlZHMpPj01LCI1KyIsYmVkcyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU9cGFzdGUwKGJlZHMsIGlmZWxzZShkb3dudG93biwiIGRvd250b3duIiwiIikpKQojIHRvdGFscyA8LSBwbG90X2RhdGEgJT4lCiMgICBncm91cF9ieShkb3dudG93bixiZWRzKSAlPiUgc3VtbWFyaXplKHByaWNlLHN1YikKICBnZ3Bsb3QocGxvdF9kYXRhLCBhZXMoeD1wb3N0X2RhdGUsIHk9cHJpY2UsIGNvbG9yPXR5cGUsZ3JvdXA9dHlwZSkpICsgCiAgZ2VvbV9wb2ludChzaXplPTAuMDEsYWxwaGE9MC4yKSArCiAgICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJQYWlyZWQiKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgc2UgPSBGQUxTRSkgKwogICAgdGhlbWVfYncoKQpgYGAKCgoKIyMgTWFwCgpgYGB7ciwgaW5jbHVkZT1GQUxTRX0KYmdfY29sb3I9IiNjMGMwYzAiCnRoZW1lX29wdHM8LWxpc3QodGhlbWUocGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICAjcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgI2J1Zywgbm90IHdvcmtpbmcKICAgICAgICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGNvbG91ciA9IGJnX2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSBiZ19jb2xvciwgY29sb3VyID0gTkEpLAogICAgICAgICAgICAgICAgICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPWJnX2NvbG9yLCBzaXplPTEsbGluZXR5cGU9InNvbGlkIiksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSkpCmBgYAoKCgoKYGBge3IgcHJpY2VfbWFwLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD01LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHNmKQpsaWJyYXJ5KGdncGxvdDIpCgpnZW89Z2V0X2NlbnN1cyhkYXRhc2V0ID0gJ0NBMTYnLHJlZ2lvbnM9bGlzdChDTUE9IjU5OTMzIiksZ2VvX2Zvcm1hdD0nc2YnLGxldmVsPSJSZWdpb25zIikKCmxzIDwtIGdldF9saXN0aW5ncyhzdGFydF90aW1lLGVuZF90aW1lLGdlbyRnZW9tZXRyeSxiZWRzPWMoMSksZmlsdGVyID0gJ3VuZnVybmlzaGVkJykKICBzdW1tYXJ5PWxzICU+JSBhcy5kYXRhLmZyYW1lICU+JSBzZWxlY3QoInByaWNlIiwiYmVkcyIpICU+JSBncm91cF9ieShiZWRzKSAlPiUgc3VtbWFyaXplKG1lZGlhbj1wYXN0ZTAoIiQiLGZvcm1hdChtZWRpYW4ocHJpY2UpLGJpZy5tYXJrPSIsIikpKSAlPiUgbXV0YXRlKG5hbWU9cm93JG5hbWUpCgpjdHM9Z2V0X2NlbnN1cyhkYXRhc2V0ID0gJ0NBMTYnLHJlZ2lvbnM9bGlzdChDTUE9IjU5OTMzIiksZ2VvX2Zvcm1hdD0nc2YnLGxldmVsPSJDU0QiKQoKbWluX2xpc3RpbmdzPTEwCgptZWRpYW5fcmVudCA8LSBmdW5jdGlvbih2KXsKICByZXN1bHQgPC0gaWZlbHNlKGxlbmd0aCh2KT5taW5fbGlzdGluZ3MsIG1lZGlhbih2KSxOQSkKICByZXR1cm4ocmVzdWx0KQp9CgphZ2dyZWdhdGVfbGlzdGluZ3MgPC0gYWdncmVnYXRlKGN0cyAlPiUgc2VsZWN0KCJHZW9VSUQiKSxscyxmdW5jdGlvbih4KXt4fSkKCmRhdGEgPC0gYWdncmVnYXRlKGxzICU+JSBzZWxlY3QoInByaWNlIiksY3RzLG1lZGlhbl9yZW50KQoKCmN1dG9mZnM9YXMuaW50ZWdlcihxdWFudGlsZShkYXRhJHByaWNlLCBwcm9icz1zZXEoMCwxLDAuMSksIG5hLnJtPVRSVUUpKQpsYWJlbHM9ZmFjdG9yKGFzLmNoYXJhY3RlcihzZXEoMSxsZW5ndGgoY3V0b2ZmcyktMSkgJT4lIGxhcHBseShmdW5jdGlvbihpKXtyZXR1cm4ocGFzdGUwKGN1dG9mZnNbaV0sIiAtICIsY3V0b2Zmc1tpKzFdKSl9KSksb3JkZXI9VFJVRSkKY29sb3JzPXNldE5hbWVzKFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbChsZW5ndGgobGFiZWxzKSwiUmRZbEJ1IiksbGFiZWxzKQpkYXRhJGRpc2NyZXRlX3ByaWNlPSBkYXRhJHByaWNlICU+JSBjdXQoYnJlYWtzPWN1dG9mZnMsIGxhYmVscz1sYWJlbHMpCgoKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1jdHMsIGZpbGw9IiM4MDgwODAiLCBzaXplPTAuMSkgKwogIGdlb21fc2YoZGF0YT1kYXRhLCBhZXMoZmlsbCA9IGRpc2NyZXRlX3ByaWNlKSwgc2l6ZT0wLjEpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSdSZFlsQnUnLCBkaXJlY3Rpb249LTEsIG5hLnZhbHVlPSIjODA4MDgwIixuYW1lPSJNZWRpYW4gUHJpY2UiKSArCiAgbGFicyh0aXRsZT0iQXVndXN0IFN0dWRpbyBhbmQgMSBCZWRyb29tIFVuZnVybmlzaGVkIE1lZGlhbiBBc2siKSArCiAgdGhlbWVfb3B0cwpgYGAKCgoKIyMgUmVudCBkaXN0cmlidXRpb25zIGJ5IG11bmljaXBhbGl0eQoKYGBge3IsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZ2diZWVzd2FybSkKbGlicmFyeShncmlkRXh0cmEpCgpyZWdpb25zPWFzX2NlbnN1c19yZWdpb25fbGlzdChzZWFyY2hfY2Vuc3VzX3JlZ2lvbnMoIlZhbmNvdXZlciIsJ0NBMTYnLCdDTUEnKSkKCmdlbz1nZXRfY2Vuc3VzKGRhdGFzZXQgPSAnQ0ExNicscmVnaW9ucz1yZWdpb25zLGdlb19mb3JtYXQ9J3NmJyxsZXZlbD0iUmVnaW9ucyIpCgpscyA8LSBnZXRfbGlzdGluZ3Moc3RhcnRfdGltZSxlbmRfdGltZSxnZW8kZ2VvbWV0cnksYmVkcz1jKDEpLGZpbHRlciA9ICd1bmZ1cm5pc2hlZCcsc2FuaXR5PWMoNDAwLDQwMDApKQogIHN1bW1hcnk9bHMgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIHNlbGVjdCgicHJpY2UiLCJiZWRzIikgJT4lIGdyb3VwX2J5KGJlZHMpICU+JSBzdW1tYXJpemUobWVkaWFuPXBhc3RlMCgiJCIsZm9ybWF0KG1lZGlhbihwcmljZSksYmlnLm1hcms9IiwiKSkpICU+JSBtdXRhdGUobmFtZT1yb3ckbmFtZSkKCiAgCmdlb3M9Z2V0X2NlbnN1cyhkYXRhc2V0ID0gJ0NBMTYnLHJlZ2lvbnM9cmVnaW9ucyxnZW9fZm9ybWF0PSdzZicsbGV2ZWw9IkNTRCIpICU+JQogIHN0X2pvaW4obHMpIAoKdG9wX211bmlzIDwtIGdlb3MgJT4lIGdyb3VwX2J5KG5hbWUpICU+JSBzdW1tYXJpemUoY291bnQ9bGVuZ3RoKG5hbWUpKSAlPiUgCiAgdG9wX24oNSxjb3VudCkgJT4lIHB1bGwoIm5hbWUiKQoKcGxvdF9kYXRhIDwtIGdlb3MgJT4lIGZpbHRlcihuYW1lICVpbiUgdG9wX211bmlzKSAlPiUKICByZW5hbWUoTXVuaWNpcGFsaXR5PW5hbWUpCnRpdGxlPSJEaXN0cmlidXRpb24gb2YgVW5mdXJuaXNoZWQgMWJyIFJlbnRzLCBTZXB0ZW1iZXIgMjAxNyIKcDEgPC0gZ2dwbG90KHBsb3RfZGF0YSkgKyAKICBnZW9tX2RlbnNpdHkoYWVzKHg9cHJpY2UsIGNvbG9yPU11bmljaXBhbGl0eSkpICsKICBsYWJzKHRpdGxlPXRpdGxlKQpwMiA8LSBnZ3Bsb3QocGxvdF9kYXRhLCBhZXMoTXVuaWNpcGFsaXR5LCBwcmljZSkpKyAKICBnZW9tX3Zpb2xpbihhZXMoZmlsbD1NdW5pY2lwYWxpdHkgKSkgKyAKICAjZ2VvbV9iZWVzd2FybShwY2ggPSAxLCBjb2w9J3doaXRlJywgY2V4PTAuOCwgYWxwaGE9MC42KSArCiAgbGFicyh0aXRsZT10aXRsZSkKZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbmNvbD0xKQpgYGAKCgoKIyMgTG9va2luZyBpbnRvIENvcXVpdGxhbQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcmVnaW9uX25hbWU9IkNvcXVpdGxhbSIgIyJSaWNobW9uZCIKcmVnaW9ucz1hc19jZW5zdXNfcmVnaW9uX2xpc3Qoc2VhcmNoX2NlbnN1c19yZWdpb25zKHJlZ2lvbl9uYW1lLCdDQTE2JywnQ1NEJykgJT4lIGZpbHRlcihuYW1lPT1yZWdpb25fbmFtZSkpCgpnZW89Z2V0X2NlbnN1cyhkYXRhc2V0ID0gJ0NBMTYnLHJlZ2lvbnM9cmVnaW9ucyxnZW9fZm9ybWF0PSdzZicsbGV2ZWw9IlJlZ2lvbnMiKQoKbHMgPC0gZ2V0X2xpc3RpbmdzKHN0YXJ0X3RpbWUsZW5kX3RpbWUsZ2VvJGdlb21ldHJ5LGJlZHM9YygxKSxmaWx0ZXIgPSAndW5mdXJuaXNoZWQnLHNhbml0eT1jKDQwMCw0MDAwKSkKc3VtbWFyeT1scyAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgCiAgc2VsZWN0KCJwcmljZSIsImJlZHMiKSAlPiUgCiAgZ3JvdXBfYnkoYmVkcykgJT4lCiAgc3VtbWFyaXplKG1lZGlhbj1wYXN0ZTAoIiQiLGZvcm1hdChtZWRpYW4ocHJpY2UpLGJpZy5tYXJrPSIsIikpKSAlPiUgCiAgbXV0YXRlKG5hbWU9cm93JG5hbWUpCgpjdXRvZmZzPWMoNDAwLDEzNTAsNDAwMCkKbGFiZWxzPWZhY3Rvcihhcy5jaGFyYWN0ZXIoc2VxKDEsbGVuZ3RoKGN1dG9mZnMpLTEpICU+JSBsYXBwbHkoZnVuY3Rpb24oaSl7cmV0dXJuKHBhc3RlMChjdXRvZmZzW2ldLCIgLSAiLGN1dG9mZnNbaSsxXSkpfSkpLG9yZGVyPVRSVUUpCmNvbG9ycz1zZXROYW1lcyhjKCJ0dXJxdW9pc2UiLCJwdXJwbGUiKSxsYWJlbHMpCmxzJGRpc2NyZXRlX3ByaWNlPSBscyRwcmljZSAlPiUgY3V0KGJyZWFrcz1jdXRvZmZzLCBsYWJlbHM9bGFiZWxzKQoKI2xzIDwtIGNiaW5kKGxzLHN0X2Nvb3JkaW5hdGVzKHN0X3RyYW5zZm9ybShscywxMDIwMDIpJGxvY2F0aW9uKSkKbHMgPC0gY2JpbmQobHMsc3RfY29vcmRpbmF0ZXMobHMkbG9jYXRpb24pKQoKbGlicmFyeShnZ21hcCkKCmBgYAoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmJhc2UgPC0gZ2V0X21hcChwYXN0ZTAocmVnaW9uX25hbWUsIiwgQ2FuYWRhIiksIHpvb209MTIsIHNvdXJjZSA9ICJzdGFtZW4iLCBtYXB0eXBlID0gInRvbmVyIiwgCiAgICBjcm9wID0gVCkKCiNnZ3Bsb3QoKSArCiAgZ2dtYXAoYmFzZSkgKwogICNnZW9tX3NmKGRhdGE9Z2VvLCBmaWxsPSIjODA4MDgwIiwgc2l6ZT0wLjEpICsKICAjY29vcmRfc2YoY3JzPXN0X2NycygxMDIwMDIpKSArCiAgZ2VvbV9wb2ludChkYXRhPWxzICwgYWVzKGNvbG9yID0gZGlzY3JldGVfcHJpY2UsIHg9WCwgeT1ZKSwgc2hhcGU9MjEsIHNpemU9MikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHBhbGV0dGU9Y29sb3JzKSArCiAgbGFicyh0aXRsZT0iQXVndXN0IFN0dWRpbyBhbmQgMSBCZWRyb29tIFVuZnVybmlzaGVkIE1lZGlhbiBBc2siLGNvbG9yPSJQcmljZSIpICsKICB0aGVtZV9vcHRzCmBgYAoKIyMgUmVudCBkaXN0cmlidXRpb25zIG92ZXIgdGltZQoKYGBge3IsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTcsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgpyZWdpb25fbmFtZT0iVmFuY291dmVyIiAKcmVnaW9ucz1hc19jZW5zdXNfcmVnaW9uX2xpc3Qoc2VhcmNoX2NlbnN1c19yZWdpb25zKHJlZ2lvbl9uYW1lLCdDQTE2JywnQ1NEJykgJT4lIGZpbHRlcihuYW1lPT1yZWdpb25fbmFtZSkpCgpnZW89Z2V0X2NlbnN1cyhkYXRhc2V0ID0gJ0NBMTYnLHJlZ2lvbnM9cmVnaW9ucyxnZW9fZm9ybWF0PSdzZicsbGV2ZWw9IlJlZ2lvbnMiKQoKbHMgPC0gZ2V0X2xpc3RpbmdzKHN0YXJ0X3RpbWUsZW5kX3RpbWUsZ2VvJGdlb21ldHJ5LGJlZHM9YygxKSxmaWx0ZXIgPSAndW5mdXJuaXNoZWQnLHNhbml0eT1jKDQwMCw0MDAwKSkKCmxzJHllYXJfbW9udGggPC0gZmFjdG9yKHN1YnN0cihscyRwb3N0X2RhdGUsMCw3KSxvcmRlcmVkID0gVFJVRSkKI2xzJHllYXJfbW9udGhfZGF5IDwtIGZhY3RvcihzdWJzdHIobHMkcG9zdF9kYXRlLDAsMTApLG9yZGVyZWQgPSBUUlVFKQogCiNscyAlPiUgZ3JvdXBfYnkoeWVhcl9tb250aCkgJT4lIHN1bW1hcml6ZShjb3VudD1sZW5ndGgoeWVhcl9tb250aCkpICU+JSBhcy5kYXRhLmZyYW1lICU+JSBzZWxlY3QoInllYXJfbW9udGgiLCJjb3VudCIpCiNscyAlPiUgZ3JvdXBfYnkoeWVhcl9tb250aF9kYXkpICU+JSBzdW1tYXJpemUoY291bnQ9bGVuZ3RoKHllYXJfbW9udGhfZGF5KSkgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIHNlbGVjdCgieWVhcl9tb250aF9kYXkiLCJjb3VudCIpCiAgCiAKcGxvdF9kYXRhIDwtIGxzICU+JSBhcy5kYXRhLmZyYW1lICU+JSBzZWxlY3QoInByaWNlIiwieWVhcl9tb250aCIpCnRpdGxlPSJEaXN0cmlidXRpb24gb2YgVW5mdXJuaXNoZWQgMWJyIFJlbnRzLCBDaXR5IG9mIFZhbmNvdXZlciIKcDEgPC0gZ2dwbG90KHBsb3RfZGF0YSkgKyAKICBnZW9tX2RlbnNpdHkoYWVzKHg9cHJpY2UsIGNvbG9yPXllYXJfbW9udGgpKSArCiAgbGFicyh0aXRsZT10aXRsZSwgY29sb3I9IlllYXItTW9udGgiKQpwMiA8LSBnZ3Bsb3QocGxvdF9kYXRhLCBhZXMoeWVhcl9tb250aCwgcHJpY2UpKSsgCiAgZ2VvbV92aW9saW4oYWVzKGZpbGw9eWVhcl9tb250aCApKSArIAogICNnZW9tX2JlZXN3YXJtKHBjaCA9IDEsIGNvbD0nd2hpdGUnLCBjZXg9MC44LCBhbHBoYT0wLjYpICsKICBsYWJzKHRpdGxlPXRpdGxlLCBmaWxsPSJZZWFyLU1vbnRoIiwgeD0iWWVhci1Nb250aCIpCmdyaWQuYXJyYW5nZShwMSwgcDIsIG5jb2w9MSkKYGBgCgoKCiMjIFJlbnQgZGlzdHJpYnV0aW9ucyBieSBtdW5pY2lwYWxpdHkKCmBgYHtyLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD03LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGdnYmVlc3dhcm0pCmxpYnJhcnkoZ3JpZEV4dHJhKQoKcmVnaW9uX25hbWVzPWMoIlZhbmNvdXZlciIsIlRvcm9udG8iLCJWaWN0b3JpYSIsIkNhbGdhcnkiKQpyZWdpb25zPSBhc19jZW5zdXNfcmVnaW9uX2xpc3QoZG8uY2FsbChyYmluZCxsYXBwbHkocmVnaW9uX25hbWVzLGZ1bmN0aW9uKHJlZ2lvbl9uYW1lKXtyZXR1cm4oKHNlYXJjaF9jZW5zdXNfcmVnaW9ucyhyZWdpb25fbmFtZSwnQ0ExNicsJ0NTRCcpICU+JSBmaWx0ZXIobmFtZT09cmVnaW9uX25hbWUpKSl9KSkpCgpnZW89Z2V0X2NlbnN1cyhkYXRhc2V0ID0gJ0NBMTYnLHJlZ2lvbnM9cmVnaW9ucyxnZW9fZm9ybWF0PSdzZicsbGV2ZWw9IlJlZ2lvbnMiKQoKZ2VvbWV0cnk9c3RfdW5pb24oZ2VvJGdlb21ldHJ5KQoKYmVkcz0yCmxzIDwtIGdldF9saXN0aW5ncyhzdGFydF90aW1lLGVuZF90aW1lLGdlb21ldHJ5LGJlZHM9YyhiZWRzKSxmaWx0ZXIgPSAndW5mdXJuaXNoZWQnLHNhbml0eT1jKDQwMCw1MDAwKSkKICBzdW1tYXJ5PWxzICU+JSBhcy5kYXRhLmZyYW1lICU+JSBzZWxlY3QoInByaWNlIiwiYmVkcyIpICU+JSBncm91cF9ieShiZWRzKSAlPiUgc3VtbWFyaXplKG1lZGlhbj1wYXN0ZTAoIiQiLGZvcm1hdChtZWRpYW4ocHJpY2UpLGJpZy5tYXJrPSIsIikpKSAlPiUgbXV0YXRlKG5hbWU9cm93JG5hbWUpCgogIApnZW9zPWdldF9jZW5zdXMoZGF0YXNldCA9ICdDQTE2JyxyZWdpb25zPXJlZ2lvbnMsZ2VvX2Zvcm1hdD0nc2YnLGxldmVsPSJDU0QiKSAlPiUKICBzdF9qb2luKGxzKSAKCgpwbG90X2RhdGEgPC0gZ2VvcyAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgc2VsZWN0KCJuYW1lIiwicHJpY2UiKSAlPiUKICByZW5hbWUoTXVuaWNpcGFsaXR5PW5hbWUpCnRpdGxlPXBhc3RlMCgiRGlzdHJpYnV0aW9uIG9mIFVuZnVybmlzaGVkICIsYmVkcywiYnIgUmVudHMsIEF1Z3VzdCAyMDE3IikKcDEgPC0gZ2dwbG90KHBsb3RfZGF0YSkgKyAKICBnZW9tX2RlbnNpdHkoYWVzKHg9cHJpY2UsIGNvbG9yPU11bmljaXBhbGl0eSkpICsKICBsYWJzKHRpdGxlPXRpdGxlKQpwMiA8LSBnZ3Bsb3QocGxvdF9kYXRhLCBhZXMoTXVuaWNpcGFsaXR5LCBwcmljZSkpKyAKICBnZW9tX3Zpb2xpbihhZXMoZmlsbD1NdW5pY2lwYWxpdHkgKSkgKyAKICAjZ2VvbV9iZWVzd2FybShwY2ggPSAxLCBjb2w9J3doaXRlJywgY2V4PTAuOCwgYWxwaGE9MC42KSArCiAgbGFicyh0aXRsZT10aXRsZSkKZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbmNvbD0xKQpgYGAKCgojIyBDaGVja2luZyBTcGVjaWZpYyBhcmVhCgoKYGBge3J9CiNnZW89c2Y6OnJlYWRfc2YoIi4uL2RhdGEvY3VzdG9tX3JlZ2lvbi5nZW9qc29uIikKZ2VvPXNmOjpyZWFkX3NmKCIuLi9kYXRhL3ZpY3RvcmlhX3N0YWluc2J1cnkuZ2VvanNvbiIpCgoKCmxzIDwtIGdldF9saXN0aW5ncygiMjAxNy0wNi0wMSIsIjIwMTctMDktMDEiLGdlbyRnZW9tZXRyeSxmaWx0ZXIgPSAndW5mdXJuaXNoZWQnKQogIHN1bW1hcnk9bHMgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIHNlbGVjdCgicHJpY2UiLCJiZWRzIikgJT4lIGdyb3VwX2J5KGJlZHMpICU+JSBzdW1tYXJpemUobWVkaWFuPXBhc3RlMCgiJCIsZm9ybWF0KG1lZGlhbihwcmljZSksYmlnLm1hcms9IiwiKSkpICU+JSBtdXRhdGUobmFtZT1yb3ckbmFtZSkKCiAgZ2dwbG90KGxzLCBhZXMoYmVkcywgcHJpY2UpKSsgCiAgZ2VvbV92aW9saW4oYWVzKGZpbGw9YmVkcyApKSArIAogICNnZW9tX2JlZXN3YXJtKHBjaCA9IDEsIGNvbD0nd2hpdGUnLCBjZXg9MC44LCBhbHBoYT0wLjYpICsKICBsYWJzKHRpdGxlPSJKdW5lLUF1Z3VzdCAxYnIgdW5mdXJuaXNoZWQgQ3VzdG9tIFJlZ2lvbiIpCiAgCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTV9CgoKY3V0b2Zmcz1jKDQwMCwxMDUwLDQwMDApCmxhYmVscz1mYWN0b3IoYXMuY2hhcmFjdGVyKHNlcSgxLGxlbmd0aChjdXRvZmZzKS0xKSAlPiUgbGFwcGx5KGZ1bmN0aW9uKGkpe3JldHVybihwYXN0ZTAoY3V0b2Zmc1tpXSwiIC0gIixjdXRvZmZzW2krMV0pKX0pKSxvcmRlcj1UUlVFKQpjb2xvcnM9c2V0TmFtZXMoYygidHVycXVvaXNlIiwicHVycGxlIiksbGFiZWxzKQpscyRkaXNjcmV0ZV9wcmljZT0gbHMkcHJpY2UgJT4lIGN1dChicmVha3M9Y3V0b2ZmcywgbGFiZWxzPWxhYmVscykKbHMgPC0gY2JpbmQobHMsc3RfY29vcmRpbmF0ZXMobHMkbG9jYXRpb24pKQoKYmFzZSA8LSBnZXRfbWFwKGxvY2F0aW9uPWMoLTEyMi44NDU5NDUzNTgyNzYzNywgNDkuMTg0MjI4MDE2MTY4MTgpLCB6b29tPTE1LCBzb3VyY2UgPSAic3RhbWVuIiwgbWFwdHlwZSA9ICJ0b25lciIsIAogICAgY3JvcCA9IFQpCgojZ2dwbG90KCkgKwogIGdnbWFwKGJhc2UpICsKICAjZ2VvbV9zZihkYXRhPWdlbywgZmlsbD0iIzgwODA4MCIsIHNpemU9MC4xKSArCiAgI2Nvb3JkX3NmKGNycz1zdF9jcnMoMTAyMDAyKSkgKwogIGdlb21fcG9pbnQoZGF0YT1scyAsIGFlcyhjb2xvciA9IGRpc2NyZXRlX3ByaWNlLCB4PVgsIHk9WSksIHNoYXBlPTIxLCBzaXplPTQpICsKICBzY2FsZV9maWxsX21hbnVhbChwYWxldHRlPWNvbG9ycykgKwogIGdlb21fcG9seWdvbihkYXRhPSBmb3J0aWZ5KGFzKGdlbywiU3BhdGlhbCIpKSwgYWVzKHg9bG9uZywgeT1sYXQpLCBmaWxsPU5BLCBzaXplPTAuNSxjb2xvcj0nYmx1ZScpICsKICBsYWJzKHRpdGxlPSJBdWd1c3QgMSBCZWRyb29tIFVuZnVybmlzaGVkIE1lZGlhbiBBc2siLGNvbG9yPSJQcmljZSIpICsKICB0aGVtZV9vcHRzCgpgYGAKCgoKYGBge3J9Cm15X3RoZW1lIDwtIGxpc3QoCiAgdGhlbWVfbWluaW1hbCgpLAogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJ3aGl0ZSIpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJ3aGl0ZSIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSkKKQpgYGAKCgojIyBWYW5jb3V2ZXIgcmVudCBtYXAKYGBge3IsIGVjaG89RkFMU0UsIG1lc3NhZ2U9VFJVRSwgd2FybmluZz1UUlVFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShjYW5jZW5zdXMpCmxpYnJhcnkocmVudGFsKQpsaWJyYXJ5KHNmKQpyZWdpb25zPWxpc3RfY2Vuc3VzX3JlZ2lvbnMoJ0NBMTYnLCB1c2VfY2FjaGUgPSBUUlVFKSAlPiUgZmlsdGVyKGxldmVsPT0iQ01BIixuYW1lID09ICJWYW5jb3V2ZXIiKQpnZW89Z2V0X2NlbnN1cyhkYXRhc2V0ID0gJ0NBMTYnLHJlZ2lvbnM9YXNfY2Vuc3VzX3JlZ2lvbl9saXN0KHJlZ2lvbnMpLGdlb19mb3JtYXQ9J3NmJyxsZXZlbD0iQ1QiKQoKc3RhcnRfdGltZT0iMjAxNy0wOS0wMSIKZW5kX3RpbWU9IjIwMTctMTItMDciCgpwcHNmX2Zvcm1hdHRlciA8LSBmdW5jdGlvbih4KXtyZXR1cm4ocGFzdGUwKCIkIixyb3VuZCh4LDIpLCIvc2YiKSl9CgpscyA8LSBnZXRfbGlzdGluZ3Moc3RhcnRfdGltZSxlbmRfdGltZSxzdF91bmlvbihnZW8kZ2VvbWV0cnkpLGJlZHM9YygnMCcsJzEnLCcyJyksZmlsdGVyID0gJ3VuZnVybmlzaGVkJykgJT4lCiAgbXV0YXRlKHBwc2Y9cHJpY2Uvc2l6ZSkKICAKZ2VvX2xpc3RpbmdzIDwtIHN0X2pvaW4oZ2VvLCBmaWx0ZXIobHMsIWlzLm5hKHBwc2YpKSkgJT4lIAogIGdyb3VwX2J5KEdlb1VJRCkgJT4lIAogIHN1bW1hcml6ZShwcHNmPW1lZGlhbihwcHNmKSxjb3VudD1uKCkpCgpnZ3Bsb3QoZ2VvX2xpc3RpbmdzICU+JSAgbXV0YXRlKHBwc2Y9aWZlbHNlKGNvdW50Pj01LHBwc2YsTkEpKSAlPiUgc3RfYXNfc2YpICsKICBnZW9tX3NmKGFlcyhmaWxsPXBwc2YpLHNpemU9TkEpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhuYW1lPSJBc2tpbmcgUmVudC9zZiIsIG9wdGlvbj0ibWFnbWEiKSArCiAgbGFicyh0aXRsZT0iT2N0b2JlciAxIHRocm91Z2ggRGVjIDcgQXNraW5nIFJlbnQvc2YiKSArCiAgbXlfdGhlbWUKCgpgYGAK